#include "Tab.h"
#include <CommCtrl.h>
#include "TabPage.h"
#include "WndProcPicker.h"




namespace Zaf {
namespace Mwl {



const wchar_t* const Tab::ClassName = L"SysTabControl32";
__Internal::WndProcPicker Tab::TabWndProc(ClassName);



Tab::Tab() {
	CommonConstructor();
}



Tab::Tab(WORD id) : ControlWindow(id) {
	CommonConstructor();
}



Tab::~Tab() {

}



void Tab::CommonConstructor() {

	ChangeStyle(TCS_TABS, true);
}



HWND Tab::CreateConcreteWindow() {

	unsigned int x;
	unsigned int y;
	GetPosition(x, y);

	unsigned int width;
	unsigned int height;
	GetSize(width, height);

	return CreateWindowEx(
		GetExStyle(),
		ClassName,
		GetText().c_str(),
		GetStyle(),
		x,
		y,
		width,
		height,
		GetParentOrOwner(),
		(HMENU)GetID(),
		GetModuleHandle(nullptr),
		0
	);
}



TabAlignment Tab::GetAlignment() const {

	DWORD style = GetStyle();

	if ((style & TCS_VERTICAL) != 0 && 
		(style & TCS_MULTILINE) != 0 &&
		(style & TCS_BOTTOM) != 0) {
	
			return TabAlignment::Right;
	}

	if ((style & TCS_VERTICAL) != 0 && 
		(style & TCS_MULTILINE) != 0) {
	
			return TabAlignment::Left;
	}

	if ((style & TCS_BOTTOM) != 0) {
		return TabAlignment::Bottom;
	}

	return TabAlignment::Top;
}

void Tab::SetAlignment(TabAlignment value) {

	ChangeStyle(TCS_VERTICAL, false);
	ChangeStyle(TCS_BOTTOM, false);
	ChangeStyle(TCS_MULTILINE, false);

	switch (value) {

	case TabAlignment::Right:
		ChangeStyle(TCS_VERTICAL, true);
		ChangeStyle(TCS_BOTTOM, true);
		ChangeStyle(TCS_MULTILINE, true);
		break;

	case TabAlignment::Left:
		ChangeStyle(TCS_VERTICAL, true);
		ChangeStyle(TCS_MULTILINE, true);
		break;

	case TabAlignment::Bottom:
		ChangeStyle(TCS_BOTTOM, true);
		break;

	default:
		break;
	}

	ShowPage(GetSelectedIndex());
}



TabAppearance Tab::GetAppearance() const {

	DWORD style = GetStyle();

	if ((style & TCS_FLATBUTTONS) != 0) {
		return TabAppearance::FlatButtons;
	}

	if ((style & TCS_BUTTONS) != 0) {
		return TabAppearance::Buttons;
	}

	return TabAppearance::Tabs;
}

void Tab::SetAppearance(TabAppearance value) {

	ChangeStyle(TCS_TABS, false);
	ChangeStyle(TCS_BUTTONS, false);
	ChangeStyle(TCS_FLATBUTTONS, false);

	switch (value) {

	case TabAppearance::FlatButtons:
		ChangeStyle(TCS_BUTTONS | TCS_FLATBUTTONS, true);
		break;
		
	case TabAppearance::Buttons:
		ChangeStyle(TCS_BUTTONS, true);
		break;

	default:
		ChangeStyle(TCS_TABS, true);
		break;
	}

	ShowPage(GetSelectedIndex());
}



int Tab::InsertPage(TabPage& page, int pos) {

	//set page's parent to tab's parent
	Window* parentWindow = (Window*)GetWindowLong(this->GetParentOrOwner(), GWL_USERDATA);
	page.SetParent(*parentWindow);

	std::wstring text = page.GetText();

	TCITEM item = { 0 };
	item.mask = TCIF_TEXT | TCIF_PARAM;
	item.pszText = (wchar_t*)text.c_str();
	item.lParam = (LPARAM)&page;

	int pageIndex = SendMessage(
		GetHandle(), 
		TCM_INSERTITEM, 
		pos == -1 ? GetCount() : pos, 
		(LPARAM)&item
	);

	//make first page visible
	if (GetCount() == 1) {
		ShowPage(0);
	}

	return pageIndex;
}



bool Tab::DeletePage(int index) {
	return SendMessage(GetHandle(), TCM_DELETEITEM, index, 0) == TRUE;
}



TabPage* Tab::GetPage(int pos) const {

	TCITEM item = { 0 };
	item.mask = TCIF_PARAM;

	if (SendMessage(GetHandle(), TCM_GETITEM, pos, (LPARAM)&item) == FALSE) {
		return nullptr;
	}

	return (TabPage*)item.lParam;
}


int Tab::GetSelectedIndex() const {
	return SendMessage(GetHandle(), TCM_GETCURSEL, 0, 0);
}

void Tab::SetSelectedIndex(int value) {

	int preIndex = SendMessage(GetHandle(), TCM_SETCURSEL, value, 0);

	if (preIndex != -1) {
		HidePage(preIndex);
		ShowPage(value);
	}
}



void Tab::Clear() {
	SendMessage(GetHandle(), TCM_DELETEALLITEMS, 0, 0);
}



int Tab::GetCount() const {
	return SendMessage(GetHandle(), TCM_GETITEMCOUNT, 0, 0);
}



void Tab::AdjustPage(TabPage* page) {

	RECT tabRect;
	GetWindowRect(GetHandle(), &tabRect);
	SendMessage(GetHandle(), TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect);

	POINT leftUpperCorner;
	leftUpperCorner.x = tabRect.left;
	leftUpperCorner.y = tabRect.top;
	ScreenToClient(GetHandle(), &leftUpperCorner);

	POINT rightBottomCorner;
	rightBottomCorner.x = tabRect.right;
	rightBottomCorner.y = tabRect.bottom;
	ScreenToClient(GetHandle(), &rightBottomCorner);

	page->SetPosition(leftUpperCorner.x, leftUpperCorner.y);
	page->SetSize(rightBottomCorner.x - leftUpperCorner.x, rightBottomCorner.y - leftUpperCorner.y);
}



LRESULT Tab::OnNotify(const WndMsg& msg) {

	NMHDR* p = (NMHDR*)msg.LParam;

	switch (p->code) {

		case TCN_SELCHANGING: {

			SelectedPageChangingArgs args;
			args.Sender = this;
			args.Cancel = false;
			SelectedPageChangingEvent.Invoke(args);

			if (args.Cancel == false) {
				HidePage(GetSelectedIndex());
			}

			return args.Cancel;
		}

		case TCN_SELCHANGE: {

			ShowPage(GetSelectedIndex());

			SelectedPageChangedArgs args;
			args.Sender = this;
			SelectedPageChangedEvent.Invoke(args);
			break;
		}
	}

	return 0;
}



void Tab::ShowPage(int index) {

	TabPage* page = GetPage(index);

	if (page != nullptr) {

		AdjustPage(page);
		page->IsVisible(true);
	}
}



void Tab::HidePage(int index) {

	TabPage* page = GetPage(index);

	if (page != nullptr) {
		page->IsVisible(false);
	}
}



LRESULT Tab::DefWndProc(const WndMsg& msg) {

	return TabWndProc(msg.Hwnd, msg.Message, msg.WParam, msg.LParam);
}



}
}